package com.jjzhk.common.server;

import com.jjzhk.common.bean.RpcRequest;
import com.jjzhk.common.bean.RpcResponse;
import com.jjzhk.common.handler.RpcDecoder;
import com.jjzhk.common.handler.RpcEncoder;
import com.jjzhk.common.handler.RpcServerHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.collections4.*;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.HashMap;
import java.util.Map;

/**
 * RpcServer
 *
 * @author : JJZHK
 * @date : 2016-08-13
 * @comments :
 **/
public class RpcServer implements ApplicationContextAware, InitializingBean {
    private Logger logger = LoggerFactory.getLogger(RpcServer.class);
    private Map<String, Object> handlerMap = new HashMap<>();
    private int port;

    public RpcServer(int port) {
        this.port = port;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workGroup = new NioEventLoopGroup();

       try {
           ServerBootstrap serverBootstrap = new ServerBootstrap();
           serverBootstrap.group(bossGroup, workGroup).channel(NioServerSocketChannel.class).option(ChannelOption.SO_BACKLOG, 1024);
           serverBootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);
           serverBootstrap.childHandler(new ChannelInitializer<SocketChannel>() {
               @Override
               protected void initChannel(SocketChannel channel) throws Exception {
                   channel.pipeline().addLast("decoder", new RpcDecoder(RpcRequest.class)); // deconder, inbound
                   channel.pipeline().addLast("encoder", new RpcEncoder(RpcResponse.class)); // encoder, outbound
                   channel.pipeline().addLast("handler", new RpcServerHandler(handlerMap)); // handler, inbound
               }
           });

           ChannelFuture future = serverBootstrap.bind(port).sync();
           logger.info("server starts on port : {}", port);
           future.channel().closeFuture().sync();
       }
       finally {
           workGroup.shutdownGracefully();
           bossGroup.shutdownGracefully();
       }
    }

    @Override
    public void setApplicationContext(ApplicationContext ctx) throws BeansException {
        Map<String, Object> serviceBeanMap = ctx.getBeansWithAnnotation(RpcService.class);
        if (MapUtils.isNotEmpty(serviceBeanMap)) {
            for (Object serviceBean : serviceBeanMap.values()) {
                RpcService rpcService = serviceBean.getClass().getAnnotation(RpcService.class);
                String serviceName = rpcService.value().getName(); // interface name
                String serviceVersion = rpcService.version();
                if (StringUtils.isNotEmpty(serviceVersion)) {
                    serviceName += "-" + serviceVersion;
                }
                handlerMap.put(serviceName, serviceBean); // serviceBean is the interface implement class
                logger.info("handlerMap is : {}, {}", serviceName, serviceBean);
            }
        }
    }
}
